﻿using System;
using Microsoft.Xna.Framework;
using ProtoGame.Input;
using ProtoGame.GameLib.Entities;
using ProtoGame.GameLib;
using ProtoGame.DescritoresContent;
using System.Diagnostics;

namespace ProtoGame.Sprites
{
    class PGSpriteJogador : AnimatedSprite
    {
        const double QtdeJogadorPisca = 11.0;
        readonly TimeSpan TempoAtacando = TimeSpan.FromSeconds(0.15);
        readonly TimeSpan InvulQuandoAtingido = TimeSpan.FromSeconds(2);


        IPGEntradasAcao entradas;
        Stopwatch tempoAtaque = new Stopwatch();
        TimeSpan tempoInvulneravel = TimeSpan.Zero;
        TimeSpan periodoTrocaCor = TimeSpan.Zero;
        
        PGAtaqueEspada spriteAtaque;
        AtributosJogador atributos;

        public BasicSprite ItemMarcador { get; set; }

        public override Rectangle Bounds
        {
            get
            {
                // HACK: código hardcoded
                Rectangle bounds = base.Bounds;
                bounds.Inflate(-9, -7);
                return bounds;
            }
        }

        public PGSpriteJogador(AtributosJogador atribs, PGAtaqueEspada ataque, IPGEntradasAcao entradas, Tileset gfx)
            : base(PGIMGKobold.CriarAnimator(), gfx)
        {
            this.entradas = entradas;
            this.atributos = atribs;
            this.spriteAtaque = ataque;
            
            ItemMarcador = null;
            CollisionType = TiposColisoes.Jogador;
            AnimationState = PGIMGKobold.StateParadoBaixo;
        }

        private void JogadorAtingido()
        {
            if (tempoInvulneravel <= TimeSpan.Zero)
            {
                atributos.Saude--;
                tempoInvulneravel = InvulQuandoAtingido;
                periodoTrocaCor = TimeSpan.FromSeconds(InvulQuandoAtingido.TotalSeconds / QtdeJogadorPisca);
                Visible = !Visible;
            }
        }

        private void ProcessaInvulnerabilidade(TimeSpan elapsed)
        {
            if (tempoInvulneravel > TimeSpan.Zero)
            {
                tempoInvulneravel -= elapsed;
                periodoTrocaCor -= elapsed;

                if (periodoTrocaCor <= TimeSpan.Zero)
                {
                    Visible = !Visible;
                    periodoTrocaCor = TimeSpan.FromSeconds(InvulQuandoAtingido.TotalSeconds / QtdeJogadorPisca);
                }
            }
            else
            {
                Visible = true;
            }
        }

        private void ProcessaState(Vector2 direcao)
        {
            if (!entradas.AcaoAtaca())
            {
                if (direcao != Vector2.Zero)
                {
                    if (Math.Abs(direcao.X) >= Math.Abs(direcao.Y))
                        AnimationState = direcao.X > 0 ? PGIMGKobold.StateAndandoDireita : PGIMGKobold.StateAndandoEsquerda;
                    else
                        AnimationState = direcao.Y < 0 ? PGIMGKobold.StateAndandoCima : PGIMGKobold.StateAndandoBaixo;
                }
                else
                {
                    switch (AnimationState)
                    {
                        case PGIMGKobold.StateAndandoBaixo:
                            AnimationState = PGIMGKobold.StateParadoBaixo;
                            break;

                        case PGIMGKobold.StateAndandoCima:
                            AnimationState = PGIMGKobold.StateParadoCima;
                            break;

                        case PGIMGKobold.StateAndandoDireita:
                            AnimationState = PGIMGKobold.StateParadoDireita;
                            break;

                        case PGIMGKobold.StateAndandoEsquerda:
                            AnimationState = PGIMGKobold.StateParadoEsquerda;
                            break;

                        default:
                            break;
                    }
                }
            }
            else
            {
                if (direcao != Vector2.Zero)
                {
                    if (Math.Abs(direcao.X) >= Math.Abs(direcao.Y))
                        AnimationState = direcao.X > 0 ? PGIMGKobold.StateAtaqueDireita : PGIMGKobold.StateAtaqueEsquerda;
                    else
                        AnimationState = direcao.Y < 0 ? PGIMGKobold.StateAtaqueCima : PGIMGKobold.StateAtaqueBaixo;
                }
                else
                {
                    switch (AnimationState)
                    {
                        case PGIMGKobold.StateParadoBaixo:
                        case PGIMGKobold.StateAndandoBaixo:
                            AnimationState = PGIMGKobold.StateAtaqueBaixo;
                            direcao.Y += 1;
                            break;

                        case PGIMGKobold.StateParadoCima:
                        case PGIMGKobold.StateAndandoCima:
                            AnimationState = PGIMGKobold.StateAtaqueCima;
                            direcao.Y -= 1;
                            break;

                        case PGIMGKobold.StateParadoDireita:
                        case PGIMGKobold.StateAndandoDireita:
                            AnimationState = PGIMGKobold.StateAtaqueDireita;
                            direcao.X += 1;
                            break;

                        case PGIMGKobold.StateParadoEsquerda:
                        case PGIMGKobold.StateAndandoEsquerda:
                            AnimationState = PGIMGKobold.StateAtaqueEsquerda;
                            direcao.X -= 1;
                            break;

                        default:
                            break;
                    }
                }

                Atacar(direcao);
            }
        }

        private void Atacar(Vector2 direcao)
        {
            spriteAtaque.Atacar(TempoAtacando, direcao, Position + PGIMGKobold.PosicaoPixelSeguraArma(AnimationState));
            spriteAtaque.LayerDepth = AnimationState == PGIMGKobold.StateAtaqueCima ? LayerDepth + 0.001f : LayerDepth;
            
            Velocity = Vector2.Zero;
            tempoAtaque.Start();
        }

        private void ProcessaDirecao(ref Vector2 direcao)
        {
            if (entradas.AcaoBaixo()) direcao.Y++;
            if (entradas.AcaoCima()) direcao.Y--;
            if (entradas.AcaoDireita()) direcao.X++;
            if (entradas.AcaoEsquerda()) direcao.X--;

            if (direcao != Vector2.Zero)
                direcao.Normalize();

            Velocity = direcao * atributos.VelocidadeMovimento;
        }

        private void ProcessaMarcador()
        {
            if (ItemMarcador != null)
            {
                if (entradas.AcaoItem())
                {
                    ItemMarcador.Position = Vector2.Zero;

                    Vector2 posicao = new Vector2(Bounds.Center.X, Bounds.Center.Y);
                    posicao -= new Vector2(ItemMarcador.Bounds.Center.X, ItemMarcador.Bounds.Center.Y);
                    
                    ItemMarcador.Position = posicao;
                    ItemMarcador.Visible = true;
                    ItemMarcador.Alive = true;
                }
            }
        }

        public override void Update(GameTime gameTime)
        {
            Vector2 direcao = Vector2.Zero;

            if (!tempoAtaque.IsRunning)
            {
                ProcessaDirecao(ref direcao);
                ProcessaState(direcao);
                ProcessaMarcador();
            }
            else
            {
                if (tempoAtaque.Elapsed >= TempoAtacando)
                    tempoAtaque.Reset();
            }

            ProcessaInvulnerabilidade(gameTime.ElapsedGameTime);

            atributos.TempoJogo += gameTime.ElapsedGameTime;

            base.Update(gameTime);
        }

        /// <summary>
        /// Algorítmo de tratamento de colisão de Sprite jogador com outro objeto qualquer
        /// </summary>
        /// <param name="collisionType">Tipo de colisão do outro objeto.</param>
        public override void DoCollision(byte collisionType)
        {
            switch (collisionType)
            {
                case TiposColisoes.Solido:
                    UndoMovement();
                    break;

                case TiposColisoes.Monstro:
                case TiposColisoes.AtaqueMonstro:
                    JogadorAtingido();
                    break;

                default:
                    break;
            }
        }
    }
}
